use hyper::http::{Response, Version};
use hyper::Body;
use mlua::{UserData, UserDataMethods};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub struct HttpResponse{
    resp:Arc<Mutex<Response<Body>>>,
    err:String,
}

impl HttpResponse{
    pub fn new(resp:Response<Body>)->Self{
        let resp = Arc::new(Mutex::new(resp));
        HttpResponse{resp,err:String::new()}
    }
    pub fn error<S:ToString>(s:S)->Self{
        let resp  = Response::new(Body::from(vec![]));
        let resp = Arc::new(Mutex::new(resp));
        HttpResponse{
            resp,
            err:s.to_string(),
        }
    }
}
impl UserData for HttpResponse {
    fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
        methods.add_method("is_err",|_,resp,()|{
            if resp.err.len() <= 0 {
                return Ok(false)
            }
            Ok(true)
        });
        methods.add_method("error",|_,resp,()|{
            Ok(resp.err.clone())
        });
        methods.add_method("status",|_,resp,()|{
            let status = resp.resp.lock().unwrap();
            Ok(status.status().as_u16())
        });
        methods.add_method("version",|_,resp,()|{
            let resp = resp.resp.lock().unwrap();
            let v = match resp.version() {
                Version::HTTP_09=> "HTTP/0.9",
                Version::HTTP_10=> "HTTP/1.0",
                Version::HTTP_11=> "HTTP/1.1",
                Version::HTTP_2=> "HTTP/2.0",
                Version::HTTP_3=> "HTTP/3.0",
                _=>"unknown",
            };
            Ok(v.to_string())
        });
        methods.add_async_method("body",|_, resp, ()|async move{
            let mut resp = resp.resp.lock().unwrap();
            let body = hyper::body::to_bytes(resp.body_mut()).await.unwrap();
            unsafe {
                Ok(String::from_utf8_unchecked(body.to_vec()))
            }
        });
        methods.add_method("get_header",|_,resp,key:String|{
            let resp = resp.resp.lock().unwrap();
            let value = resp.headers().get(&key);
            match value {
                Some(s)=>Ok(s.to_str().unwrap().to_string()),
                None=>Ok(String::new())
            }
        });
        methods.add_method("headers",|_,resp,()|{
            let mut table = HashMap::<String,String>::new();
            let resp = resp.resp.lock().unwrap();
            for (key,value) in resp.headers().iter() {
                table.insert(key.to_string(), value.to_str().unwrap().to_string());
            }
            Ok(table)
        });
    }
}